﻿#region Librerias

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using ESF.General;
using System.Drawing.Drawing2D;

#endregion


namespace ESF.ImageProcessing
{
	public static class ImageHelper
	{
		#region Ajuste

		public static void Invert (Bitmap b)
		{
			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;

			unsafe
			{
				byte * p = (byte *)(void *) Scan0;

				int nOffset = stride - b.Width*3;
				int nWidth = b.Width * 3;
	
				for(int y=0;y<b.Height;++y)
				{
					for(int x=0; x < nWidth; ++x )
					{
						p[0] = (byte)(255-p[0]);
						++p;
					}
					p += nOffset;
				}
			}

			b.UnlockBits(bmData);
		}

		public static void GrayScale(Bitmap b)
		{
			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;

			unsafe
			{
				byte * p = (byte *)(void *)Scan0;

				int nOffset = stride - b.Width*3;

				byte red, green, blue;
	
				for(int y=0;y<b.Height;++y)
				{
					for(int x=0; x < b.Width; ++x )
					{
						blue = p[0];
						green = p[1];
						red = p[2];

						p[0] = p[1] = p[2] = (byte)(.299 * red + .587 * green + .114 * blue);

						p += 3;
					}
					p += nOffset;
				}
			}

			b.UnlockBits(bmData);
		}

		public static bool Brightness(Bitmap b, int nBrightness)
		{
			if (nBrightness < -255 || nBrightness > 255)
				return false;

			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;

			int nVal = 0;

			unsafe
			{
				byte * p = (byte *)(void *)Scan0;

				int nOffset = stride - b.Width*3;
				int nWidth = b.Width * 3;

				for(int y=0;y<b.Height;++y)
				{
					for(int x=0; x < nWidth; ++x )
					{
						nVal = (int) (p[0] + nBrightness);
		
						if (nVal < 0) nVal = 0;
						if (nVal > 255) nVal = 255;

						p[0] = (byte)nVal;

						++p;
					}
					p += nOffset;
				}
			}

			b.UnlockBits(bmData);

			return true;
		}

		public static bool Contrast(Bitmap b, int nContrast)
		{
			if (nContrast < -100) return false;
			if (nContrast >  100) return false;

			double pixel = 0, contrast = (100.0+nContrast)/100.0;

			contrast *= contrast;

			int red, green, blue;
			
			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;

			unsafe
			{
				byte * p = (byte *)(void *)Scan0;

				int nOffset = stride - b.Width*3;

				for(int y=0;y<b.Height;++y)
				{
					for(int x=0; x < b.Width; ++x )
					{
						blue = p[0];
						green = p[1];
						red = p[2];
				
						pixel = red/255.0;
						pixel -= 0.5;
						pixel *= contrast;
						pixel += 0.5;
						pixel *= 255;
						if (pixel < 0) pixel = 0;
						if (pixel > 255) pixel = 255;
						p[2] = (byte) pixel;

						pixel = green/255.0;
						pixel -= 0.5;
						pixel *= contrast;
						pixel += 0.5;
						pixel *= 255;
						if (pixel < 0) pixel = 0;
						if (pixel > 255) pixel = 255;
						p[1] = (byte) pixel;

						pixel = blue/255.0;
						pixel -= 0.5;
						pixel *= contrast;
						pixel += 0.5;
						pixel *= 255;
						if (pixel < 0) pixel = 0;
						if (pixel > 255) pixel = 255;
						p[0] = (byte) pixel;					

						p += 3;
					}
					p += nOffset;
				}
			}

			b.UnlockBits(bmData);

			return true;
		}
	
		public static bool Gamma(Bitmap b, double red, double green, double blue)
		{
			if (red < .2 || red > 5) return false;
			if (green < .2 || green > 5) return false;
			if (blue < .2 || blue > 5) return false;

			byte [] redGamma = new byte [256];
			byte [] greenGamma = new byte [256];
			byte [] blueGamma = new byte [256];

			for (int i = 0; i< 256; ++i)
			{
				redGamma[i] = (byte)Math.Min(255, (int)(( 255.0 * Math.Pow(i/255.0, 1.0/red)) + 0.5));
				greenGamma[i] = (byte)Math.Min(255, (int)(( 255.0 * Math.Pow(i/255.0, 1.0/green)) + 0.5));
				blueGamma[i] = (byte)Math.Min(255, (int)(( 255.0 * Math.Pow(i/255.0, 1.0/blue)) + 0.5));
			}

			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;

			unsafe
			{
				byte * p = (byte *)(void *)Scan0;

				int nOffset = stride - b.Width*3;

				for(int y=0;y<b.Height;++y)
				{
					for(int x=0; x < b.Width; ++x )
					{
						p[2] = redGamma[ p[2] ];
						p[1] = greenGamma[ p[1] ];
						p[0] = blueGamma[ p[0] ];

						p += 3;
					}
					p += nOffset;
				}
			}

			b.UnlockBits(bmData);

			return true;
		}

		public static bool Color(Bitmap b, int red, int green, int blue)
		{
			if (red < -255 || red > 255) return false;
			if (green < -255 || green > 255) return false;
			if (blue < -255 || blue > 255) return false;

			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;

			unsafe
			{
				byte * p = (byte *)(void *)Scan0;

				int nOffset = stride - b.Width*3;
				int nPixel;

				for(int y=0;y<b.Height;++y)
				{
					for(int x=0; x < b.Width; ++x )
					{
						nPixel = p[2] + red;
						nPixel = Math.Max(nPixel, 0);
						p[2] = (byte)Math.Min(255, nPixel);

						nPixel = p[1] + green;
						nPixel = Math.Max(nPixel, 0);
						p[1] = (byte)Math.Min(255, nPixel);

						nPixel = p[0] + blue;
						nPixel = Math.Max(nPixel, 0);
						p[0] = (byte)Math.Min(255, nPixel);

						p += 3;
					}
					p += nOffset;
				}
			}

			b.UnlockBits(bmData);

			return true;
		}

		#endregion


		#region Tumbnail

		public static Bitmap ToBlackAndWhite (Image pImage)
		{
			return ToBlackAndWhite (pImage, 0.4);
		}


		public static Bitmap ToBlackAndWhite (Image pImage, double pBlackBrightness)
		{
			Bitmap imgBlackWhite;

			Color currentColor;
			

			imgBlackWhite = (Bitmap) pImage;

			for (int x = 0; x < imgBlackWhite.Width; x++)
			{
				for (int y = 0; y < imgBlackWhite.Height; y++)
				{
					currentColor = imgBlackWhite.GetPixel (x, y);

					// Si el brillo es muy bajo es un tono negro
					if (currentColor.GetBrightness () < pBlackBrightness)
					{
						imgBlackWhite.SetPixel (x, y, System.Drawing.Color.Black);
					}
					else
					{
						imgBlackWhite.SetPixel (x, y, System.Drawing.Color.White);
					}
				}
			}

			return imgBlackWhite;
		}


		public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, double pPercentage)
		{
			return ResizeImage (image, (int) Math.Ceiling (image.Width * pPercentage), (int) Math.Ceiling (image.Height * pPercentage));
		}


		public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
        {
            //a holder for the result
            Bitmap result = new Bitmap(width, height);

            //use a graphics object to draw the resized image into the bitmap
            using (Graphics graphics = Graphics.FromImage(result))
            {
                //set the resize quality modes to high quality
                graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                //draw the image into the target bitmap
                graphics.DrawImage(image, 0, 0, result.Width, result.Height);
            }

            //return the resulting bitmap
            return result;
        }


		private static bool ThumbnailCallback () { return false; }


		public static Image GenerateTumbnail (string pFilename, int pWidth, int pHeight)
		{
			return GenerateTumbnail (pFilename, null, pWidth, pHeight);
		}


		public static Image GenerateTumbnail (string pFilename, string pTumbnailGeneratedFilename, int pWidth, int pHeight)
		{
			return 
				GenerateTumbnail 
				(
					Image.FromFile (pFilename),
					pTumbnailGeneratedFilename,
					pWidth, pHeight
				);
		}
		

		public static Image GenerateTumbnail (Image pImage, double pPercentage)
		{
			return GenerateTumbnail (pImage, (int) Math.Ceiling (pImage.Width * pPercentage), (int) Math.Ceiling (pImage.Height * pPercentage));
		}
		
		
		public static Image GenerateTumbnail (Image pImage, int pWidth, int pHeight)
		{
			return GenerateTumbnail (pImage, null, pWidth, pHeight);
		}


		public static Image GenerateTumbnail (Image pImage, string pTumbnailGeneratedFilename, int pWidth, int pHeight)
		{
			Image tumbnailImage;

			int tumbnailImageHeight;
			int tumbnailImageWidth;


			if (!string.IsNullOrEmpty (pTumbnailGeneratedFilename) && File.Exists (pTumbnailGeneratedFilename))
			{
				return System.Drawing.Image.FromFile (pTumbnailGeneratedFilename);
			}

			tumbnailImageHeight = pImage.Height;
			tumbnailImageWidth = pImage.Width;

			if (pImage.Height > pHeight)
			{
				tumbnailImageHeight = pHeight;
				
				// Se aplica regla de tres para obtener la relacion entre el ancho y el alto
				tumbnailImageWidth = Convert.ToInt32 (pHeight * pImage.Width / pImage.Height);
			}
			else if (pImage.Width > pWidth)
			{
				tumbnailImageWidth = pWidth;
				
				// Se aplica regla de tres para obtener la relacion entre el alto y el ancho
				tumbnailImageHeight = Convert.ToInt32 (pWidth * pImage.Height / pImage.Width);
			}
				
			tumbnailImage =
				pImage.GetThumbnailImage
				(
					tumbnailImageWidth, tumbnailImageHeight,
					new System.Drawing.Image.GetThumbnailImageAbort (ThumbnailCallback),
					IntPtr.Zero
				);
			
			if (!string.IsNullOrEmpty (pTumbnailGeneratedFilename))
			{
				tumbnailImage.Save (pTumbnailGeneratedFilename, ImageFormat.Jpeg);
			}
			
			return tumbnailImage;
		}

		#endregion


		#region Size

		public static Image Crop (Image pImage, SliceSection pSliceSection)
		{
			Rectangle slice;


			slice = new Rectangle ();

			switch (pSliceSection)
			{
				case SliceSection.Top: 
					slice.Location = new Point (0, 0);
					slice.Size = new Size (pImage.Width, pImage.Height / 2);
					break;
				
				case SliceSection.Bottom:
					slice.Location = new Point (0, pImage.Height / 2);
					slice.Size = new Size (pImage.Width, pImage.Height / 2);
					break;

				case SliceSection.TopLeft:
					slice.Location = new Point (0, 0);
					slice.Size = new Size (pImage.Width / 2, pImage.Height / 2);
					break;

				case SliceSection.TopRight:
					slice.Location = new Point (pImage.Width / 2, 0);
					slice.Size = new Size (pImage.Width / 2, pImage.Height / 2);
					break;

				case SliceSection.BottomLeft:
					slice.Location = new Point (0, pImage.Width / 2);
					slice.Size = new Size (pImage.Width / 2, pImage.Height / 2);
					break;

				case SliceSection.BottomRight:
					slice.Location = new Point (pImage.Width / 2, pImage.Width / 2);
					slice.Size = new Size (pImage.Width / 2, pImage.Height / 2);
					break;

				// Full
				default:
					slice.Location = new Point (0, 0);
					slice.Size = new Size (pImage.Width, pImage.Height);
					break;
			}

			return Crop (pImage, slice);
		}


		public static Image Crop (Image img, Rectangle cropArea)
		{
		   Bitmap bmpImage = new Bitmap(img);
		   Bitmap bmpCrop = bmpImage.Clone(cropArea,
		   bmpImage.PixelFormat);
		   return (Image)(bmpCrop);
		}


		public static Image Resize (Image imgToResize, Size size)
		{
		   int sourceWidth = imgToResize.Width;
		   int sourceHeight = imgToResize.Height;

		   float nPercent = 0;
		   float nPercentW = 0;
		   float nPercentH = 0;

		   nPercentW = ((float)size.Width / (float)sourceWidth);
		   nPercentH = ((float)size.Height / (float)sourceHeight);

		   if (nPercentH < nPercentW)
			  nPercent = nPercentH;
		   else
			  nPercent = nPercentW;

		   int destWidth = (int)(sourceWidth * nPercent);
		   int destHeight = (int)(sourceHeight * nPercent);

		   Bitmap b = new Bitmap(destWidth, destHeight);
		   Graphics g = Graphics.FromImage((Image)b);

		   g.InterpolationMode = InterpolationMode.HighQualityBicubic;

		   g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
		   g.Dispose();

		   return (Image)b;
		}

		#endregion
	}
}
